package com.waijiaojun.tpo.service.account;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.waijiaojun.tpo.comm.Error;
import com.waijiaojun.tpo.service.account.ShiroDbRealm.ShiroUser;

/**
 * 该filter的作用类似于FormAuthenticationFilter用于oauth2客户端的身份验证控制；<br/>
 * 如果当前用户还没有身份验证，首先会判断url中是否有code（服务端返回的auth code）， 如果没有则重定向到服务端进行登录并授权，然后返回auth
 * code；<br/>
 * 接着OAuth2AuthenticationFilter会用auth code创建OAuth2Token，然后提交给Subject.login进行登录；<br/>
 * 接着OAuth2Realm会根据OAuth2Token进行相应的登录逻辑。 <br/>
 *
 * @author Peter
 */
public class WeixinOAuthAuthenticationFilter extends AuthenticatingFilter {
    private static Logger logger = LoggerFactory.getLogger(WeixinOAuthAuthenticationFilter.class);

    /**
     * errcode
     */
    public static final String ERROR_CODE_PARAM = "errcode";

    /**
     * errmsg
     */
    public static final String ERROR_MSG_PARAM = "errmsg";


    private String authcCodeParam; // oauth2 authc code参数名

    private String responseType; // oauth2服务器响应类型

    private String failureUrl; // 认证失败时跳转到的错误页面

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        logger.info("createToken");
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String code = httpRequest.getParameter(authcCodeParam);
        String state = httpRequest.getParameter("state"); // 重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节
        String host = getHost(httpRequest);
        logger.info("createToken-->>code:{},state:{},host:{}", code, state, host);

        return new WeixinOAuthToken(code, state, false, host);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        logger.info("onAccessDenied    RequestURL-->>" + httpRequest.getRequestURL() + "?" + httpRequest.getQueryString());

        String errcode = request.getParameter(ERROR_CODE_PARAM);
        String errmsg = request.getParameter(ERROR_MSG_PARAM);
        if (!StringUtils.isEmpty(errcode)) {// 如果服务端返回了错误，重定向到登录错误页面
            logger.info("onAccessDenied-->>{errcode:%s,errmsg:%s}", errcode, errmsg);
            WebUtils.issueRedirect(request, response, failureUrl + "?" + ERROR_CODE_PARAM + "=" + errcode + "&errmsg=" + errmsg);
            return false;
        }
        Subject subject = getSubject(request, response);
        if (!subject.isAuthenticated() && StringUtils.isEmpty(request.getParameter(authcCodeParam))) {
            // 如果用户没有身份验证，且没有auth code，则重定向到服务端授权
            saveRequestAndRedirectToLogin(request, response);
            return false;
        }
        // 执行父类里的登录逻辑，调用Subject.login登录
        return executeLogin(request, response);
    }

    // 登录成功后的回调方法 重定向到成功页面
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        ShiroUser user = (ShiroUser) subject.getPrincipal();
        logger.info("onLoginSuccess-->>{openid:" + user.getOpenid() + "}");
        issueSuccessRedirect(request, response);
        return false;
    }

    // 登录失败后的回调
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request, ServletResponse response) {
        logger.info("onLoginFailure-->>{message:" + ae.getMessage() + "}");
        Subject subject = getSubject(request, response);
        if (subject.isAuthenticated() || subject.isRemembered()) {
            try { // 如果身份验证成功了 则也重定向到成功页面
                issueSuccessRedirect(request, response);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        } else {
            try { // 登录失败时重定向到失败页面
                WebUtils.issueRedirect(request, response, failureUrl + "?" + ERROR_CODE_PARAM + "=" + Error.ERROR_20002 + "&errmsg=" + Error.ERROR_20002_MSG + " - " + ae.getMessage());
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
        return false;
    }

    // ----------getter and setter ---------------------------------------------
    public String getAuthcCodeParam() {
        return authcCodeParam;
    }

    public void setAuthcCodeParam(String authcCodeParam) {
        this.authcCodeParam = authcCodeParam;
    }

    public String getResponseType() {
        return responseType;
    }

    public void setResponseType(String responseType) {
        this.responseType = responseType;
    }

    public String getFailureUrl() {
        return failureUrl;
    }

    public void setFailureUrl(String failureUrl) {
        this.failureUrl = failureUrl;
    }

}
